from datetime import date, datetime, timedelta
import uuid
import random
import logging
import math
from . import db
from .models import Attendantskills, ShiftAssignment, Elderdemand, DicNursingAction, ScheduleResult, TimeSlot
from config_params import shift_config, day_night_ratio, reserve_resource_ratio, params, shift_control, shift_mode_config, rest_time_control
from sqlalchemy import func

class ThreeShiftScheduler:
    """三班制排班调度器"""

    def __init__(self):
        """初始化排班调度器"""
        # 基本配置
        self.shift_config = shift_config
        self.day_night_ratio = day_night_ratio
        self.reserve_resource_ratio = reserve_resource_ratio
        self.time_slots_per_day = params['time_slots_per_day']
        self.time_interval_minutes = params['time_interval_minutes']

        # 班制控制参数
        self.shift_control = shift_control
        self.shift_mode_config = shift_mode_config
        self.rest_time_control = rest_time_control

        # 初始化当前激活的班制模式
        self.active_mode = 'three_shift'  # 默认为三班制
        self.current_shift_times = {}

        # 复用原接口的算法参数
        self.population_size = params.get('population_size', 20)
        self.crossover_rate = params.get('crossover_rate', 0.3)
        self.mutation_rate = params.get('mutation_rate', 0.115)
        self.phenotype_limit = params.get('phenotype_limit', 200)
        self.elimination_size = params.get('elimination_size', 5)
        self.initial_temperature = params.get('initial_temperature', 100.0)
        self.cooling_rate = params.get('cooling_rate', 0.995)
        self.temperature = self.initial_temperature  # 当前温度，用于模拟退火

        # 初始化日志
        self.logger = logging.getLogger(__name__)
        if not self.logger.handlers:
            handler = logging.StreamHandler()
            formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
            handler.setFormatter(formatter)
            self.logger.addHandler(handler)
            self.logger.setLevel(logging.INFO)

        self.logger.info("初始化三班制排班调度器")
        self.logger.info(f"算法参数: population_size={self.population_size}, crossover_rate={self.crossover_rate}, "
                       f"mutation_rate={self.mutation_rate}, phenotype_limit={self.phenotype_limit}, "
                       f"initial_temperature={self.initial_temperature}, cooling_rate={self.cooling_rate}")

        # 计算班次时间
        self.calculate_shift_times()

    def parse_time(self, time_str):
        """解析时间字符串为datetime.time对象"""
        return datetime.strptime(time_str, "%H:%M").time()

    def calculate_shift_times(self):
        """根据当前配置计算每个班次的实际时间"""
        # 获取当前激活的班制模式
        active_mode = self.active_mode

        # 确定要使用的班次列表
        if active_mode == 'custom':
            if hasattr(self, 'custom_shifts') and self.custom_shifts:
                active_shifts = self.custom_shifts
            else:
                active_shifts = self.shift_mode_config['three_shift']['shifts']  # 默认使用三班制
        else:
            active_shifts = self.shift_mode_config[active_mode]['shifts']

        # 过滤启用的班次
        enabled_shifts = [shift for shift in active_shifts if shift in self.shift_control['enabled_shifts']]

        # 如果没有启用的班次，使用默认的三班制
        if not enabled_shifts:
            self.logger.warning("没有启用的班次，使用默认的三班制")
            enabled_shifts = self.shift_mode_config['three_shift']['shifts']

        # 计算每个班次的时长
        total_shifts = len(enabled_shifts)
        if total_shifts == 0:
            self.logger.warning("没有激活的班次，无法计算班次时间")
            return {}

        # 如果自动调整时间，则平均分配24小时
        if self.shift_control['auto_adjust_time']:
            hours_per_shift = 24 / total_shifts
            self.logger.info(f"自动调整时间，每个班次 {hours_per_shift} 小时")
        else:
            # 否则使用默认时长
            hours_per_shift = 8  # 默认每班8小时
            self.logger.info(f"使用默认时长，每个班次 {hours_per_shift} 小时")

        # 计算每个班次的开始和结束时间
        start_time_str = self.shift_config[enabled_shifts[0]]['start_time']
        start_time = datetime.strptime(start_time_str, "%H:%M")

        shift_times = {}
        current_time = start_time

        for shift in enabled_shifts:
            shift_start = current_time.strftime("%H:%M")

            # 计算结束时间
            current_time = current_time + timedelta(hours=hours_per_shift)
            shift_end = current_time.strftime("%H:%M")

            shift_times[shift] = {
                'start_time': shift_start,
                'end_time': shift_end,
                'duration': hours_per_shift,
                'enabled': True  # 默认启用
            }

        self.current_shift_times = shift_times
        self.logger.info(f"计算班次时间: {shift_times}")
        return shift_times

    def set_shift_mode(self, mode, custom_shifts=None, start_time=None):
        """设置班制模式

        Args:
            mode: 班制模式名称，如 'one_shift', 'two_shift', 'three_shift', 'custom'
            custom_shifts: 自定义模式下的班次列表
            start_time: 第一个班次的开始时间
        """
        if mode not in self.shift_mode_config:
            raise ValueError(f"无效的班制模式: {mode}")

        # 更新激活模式
        self.active_mode = mode

        if mode == 'custom' and custom_shifts:
            # 验证自定义班次
            for shift in custom_shifts:
                if shift not in self.shift_config:
                    raise ValueError(f"无效的班次: {shift}")
            self.custom_shifts = custom_shifts

        if start_time:
            try:
                # 验证时间格式
                datetime.strptime(start_time, "%H:%M")
                # 更新第一个班次的开始时间
                first_shift = self.shift_mode_config[mode]['shifts'][0]
                self.shift_config[first_shift]['start_time'] = start_time
            except ValueError:
                raise ValueError(f"无效的时间格式: {start_time}，请使用HH:MM格式")

        # 重新计算班次时间
        self.calculate_shift_times()
        self.logger.info(f"设置班制模式为: {mode}, 班次时间: {self.current_shift_times}")

    def update_shift_config(self, enabled_shifts=None):
        """更新班次配置

        Args:
            enabled_shifts: 启用的班次列表，例如 ['morning_shift', 'afternoon_shift']
        """
        if enabled_shifts is not None:
            # 验证班次名称
            valid_shifts = set(self.shift_config.keys())
            for shift in enabled_shifts:
                if shift not in valid_shifts:
                    raise ValueError(f"无效的班次名称: {shift}")

            # 更新启用的班次
            self.shift_control['enabled_shifts'] = enabled_shifts

            # 重新计算班次时间
            self.calculate_shift_times()

            self.logger.info(f"更新班次配置: {self.shift_control}")

    def update_shift_control(self, allow_empty_shifts=None, min_shifts=None, max_shifts=None, auto_adjust_time=None):
        """更新班次控制参数

        Args:
            allow_empty_shifts: 是否允许空班次
            min_shifts: 最小班次数
            max_shifts: 最大班次数
            auto_adjust_time: 是否自动调整班次时间
        """
        if allow_empty_shifts is not None:
            self.shift_control['allow_empty_shifts'] = allow_empty_shifts

        if min_shifts is not None:
            self.shift_control['min_shifts_per_day'] = min_shifts

        if max_shifts is not None:
            self.shift_control['max_shifts_per_day'] = max_shifts

        if auto_adjust_time is not None:
            self.shift_control['auto_adjust_time'] = auto_adjust_time
            # 如果更改了自动调整时间设置，重新计算班次时间
            self.calculate_shift_times()

        self.logger.info(f"更新班次控制参数: {self.shift_control}")

    def disable_shifts(self, shifts_to_disable):
        """临时禁用特定班次（不排班但保留时间段）

        Args:
            shifts_to_disable: 要禁用的班次列表
        """
        # 验证班次名称
        active_shifts = list(self.current_shift_times.keys())

        for shift in shifts_to_disable:
            if shift not in active_shifts:
                self.logger.warning(f"班次 {shift} 不在当前激活的班次列表中")

        # 更新班次启用状态
        for shift in self.current_shift_times:
            self.current_shift_times[shift]['enabled'] = shift not in shifts_to_disable

        self.logger.info(f"禁用班次: {shifts_to_disable}, 更新后的班次时间: {self.current_shift_times}")

    def calculate_shift_hours(self, shift_type):
        """计算班次的工作时长（小时）

        Args:
            shift_type: 班次类型

        Returns:
            float: 工作时长（小时）
        """
        # 如果班次在当前计算的时间中
        if shift_type in self.current_shift_times:
            return self.current_shift_times[shift_type]['duration']

        # 否则使用原始配置计算
        if shift_type not in self.shift_config:
            self.logger.warning(f"未知的班次类型: {shift_type}")
            return 8  # 默认返8小时

        start_time = self.parse_time(self.shift_config[shift_type]['start_time'])
        end_time = self.parse_time(self.shift_config[shift_type]['end_time'])

        # 处理跨天情况
        if end_time < start_time:
            hours = (24 - start_time.hour - start_time.minute / 60) + (end_time.hour + end_time.minute / 60)
        else:
            hours = (end_time.hour - start_time.hour) + (end_time.minute - start_time.minute) / 60

        return hours

    def check_rest_time_constraints(self, caregiver_id, target_date, target_shift):
        """检查护理员的休息时间约束

        Args:
            caregiver_id: 护理员ID
            target_date: 目标排班日期
            target_shift: 目标班次

        Returns:
            bool: 是否满足休息时间约束
        """
        # 获取护理员最近的排班记录
        recent_assignments = ShiftAssignment.query.filter(
            ShiftAssignment.caregiver_id == caregiver_id,
            ShiftAssignment.shift_date < target_date,
            ShiftAssignment.shift_date >= target_date - timedelta(days=7)  # 查看过去7天
        ).order_by(ShiftAssignment.shift_date.desc()).all()

        if not recent_assignments:
            # 没有最近的排班记录，可以安排任何班次
            return True

        # 获取最近一次的排班
        last_assignment = recent_assignments[0]
        last_shift = last_assignment.shift_type
        last_date = last_assignment.shift_date
        last_end_time = last_assignment.shift_end_time

        # 计算两个班次之间的时间间隔（小时）
        if target_shift not in self.current_shift_times:
            self.logger.warning(f"目标班次 {target_shift} 不在当前班次时间中")
            return True  # 如果目标班次不在当前班次时间中，允许安排

        target_start_time_str = self.current_shift_times[target_shift]['start_time']
        target_start_time = self.parse_time(target_start_time_str)
        target_start_datetime = datetime.combine(target_date, target_start_time)

        last_end_datetime = datetime.combine(last_date, last_end_time)

        # 处理跨天情况
        if last_end_time < self.parse_time("06:00"):
            last_end_datetime = datetime.combine(last_date + timedelta(days=1), last_end_time)

        hours_between = (target_start_datetime - last_end_datetime).total_seconds() / 3600

        # 检查基本休息时间
        min_rest = self.rest_time_control['min_rest_between_shifts']

        # 如果上一个班次是夜班，需要更长的休息时间
        if last_shift == '班次3' or last_shift == 'night_shift':
            min_rest = self.rest_time_control['min_rest_after_night_shift']

        # 检查班次转换休息时间
        # 将班次名称映射到内部名称
        shift_name_map = {
            '班次1': 'morning_shift',
            '班次2': 'afternoon_shift',
            '班次3': 'night_shift'
        }

        last_shift_internal = shift_name_map.get(last_shift, last_shift)
        target_shift_internal = shift_name_map.get(target_shift, target_shift)

        transition_key = f"{last_shift_internal}_to_{target_shift_internal}"
        if transition_key in self.rest_time_control['shift_transition_rest']:
            transition_rest = self.rest_time_control['shift_transition_rest'][transition_key]
            min_rest = max(min_rest, transition_rest)

        # 检查是否满足最小休息时间要求
        if hours_between < min_rest:
            self.logger.info(f"护理员 {caregiver_id} 不满足休息时间要求: 需要 {min_rest} 小时，实际 {hours_between:.2f} 小时")
            return False

        # 检查连续工作班次数
        consecutive_shifts = 1  # 从最近的一个班次开始计数
        current_date = last_date
        for assignment in recent_assignments[1:]:
            if (current_date - assignment.shift_date).days <= 1:  # 连续的日期
                consecutive_shifts += 1
                current_date = assignment.shift_date
            else:
                break

        if consecutive_shifts >= self.rest_time_control['max_consecutive_shifts']:
            self.logger.info(f"护理员 {caregiver_id} 已经连续工作 {consecutive_shifts} 个班次，超过最大限制")
            return False

        # 检查每周工作时间
        weekly_work_hours = 0
        for assignment in recent_assignments:
            # 计算班次工作时长
            shift_hours = self.calculate_shift_hours(assignment.shift_type)
            weekly_work_hours += shift_hours

        # 加上目标班次的工作时长
        target_shift_hours = self.calculate_shift_hours(target_shift)
        if weekly_work_hours + target_shift_hours > self.rest_time_control['max_weekly_work_hours']:
            self.logger.info(f"护理员 {caregiver_id} 每周工作时间将超过限制: {weekly_work_hours + target_shift_hours} > {self.rest_time_control['max_weekly_work_hours']}")
            return False

        # 检查每周夜班次数
        if target_shift == '班次3' or target_shift == 'night_shift':
            weekly_night_shifts = sum(1 for a in recent_assignments if a.shift_type == '班次3' or a.shift_type == 'night_shift')
            if weekly_night_shifts + 1 > self.rest_time_control['max_weekly_night_shifts']:
                self.logger.info(f"护理员 {caregiver_id} 每周夜班次数将超过限制: {weekly_night_shifts + 1} > {self.rest_time_control['max_weekly_night_shifts']}")
                return False

        # 检查周末休息要求
        if self.rest_time_control['require_weekend_rest']:
            target_weekday = target_date.weekday()
            if target_weekday >= 5:  # 5=周六，6=周日
                # 检查本月已经安排的周末班次数
                month_start = target_date.replace(day=1)
                next_month = target_date.replace(day=28) + timedelta(days=4)
                month_end = next_month.replace(day=1) - timedelta(days=1)

                weekend_shifts = ShiftAssignment.query.filter(
                    ShiftAssignment.caregiver_id == caregiver_id,
                    ShiftAssignment.shift_date >= month_start,
                    ShiftAssignment.shift_date <= month_end,
                    func.extract('dow', ShiftAssignment.shift_date).in_([5, 6])  # 周六和周日
                ).count()

                if weekend_shifts >= self.rest_time_control['max_weekend_shifts_per_month']:
                    self.logger.info(f"护理员 {caregiver_id} 本月周末班次数将超过限制: {weekend_shifts + 1} > {self.rest_time_control['max_weekend_shifts_per_month']}")
                    return False

        return True

    def get_caregivers(self):
        """获取护理员信息，限制数量"""
        from config_params import params

        all_caregivers = Attendantskills.query.all()

        # 使用与原程序相同的参数限制数量
        limit = params.get('caregiver_num', len(all_caregivers))
        caregivers = all_caregivers[:limit]

        self.logger.info(f"从数据库中获取到 {len(all_caregivers)} 个护理员，使用 {len(caregivers)} 个")
        return caregivers

    def get_elders(self):
        """获取需要安排照护的老人信息，限制数量"""
        from config_params import params

        all_elders = Elderdemand.query.filter_by(arrange='1').all()

        # 使用与原程序相同的参数限制数量
        limit = params.get('client_num', len(all_elders))
        elders = all_elders[:limit]

        self.logger.info(f"从数据库中获取到 {len(all_elders)} 个需要安排的老人，使用 {len(elders)} 个")
        return elders

    def get_nursing_actions(self):
        """获取所有护理动作信息"""
        actions = DicNursingAction.query.all()
        actions_dict = {action.id: action for action in actions}
        self.logger.info(f"从数据库中获取到 {len(actions_dict)} 个护理动作")
        return actions_dict

    def parse_skills(self, skills_str):
        """解析技能字符串为技能ID列表"""
        if not skills_str:
            return []
        try:
            return [int(skill.strip()) for skill in skills_str.split(',') if skill.strip()]
        except ValueError as e:
            self.logger.error(f"解析技能字符串失败: {skills_str}, 错误: {str(e)}")
            return []

    def get_shift_hours(self, shift_type):
        """计算班次的工作时长（小时）"""
        # 使用新的calculate_shift_hours方法
        return self.calculate_shift_hours(shift_type)

    def calculate_required_caregivers(self, total_caregivers):
        """计算每个班次所需的护理员数量

        改进的算法，确保：
        1. 每个班次都至少有一个护理员（即使总人数很少）
        2. 允许护理员轮班，同一个护理员可以在同一天内担任多个班次
        3. 保留预留资源的概念，但不影响基本的班次覆盖
        """
        # 计算各班次工作时长
        morning_hours = self.get_shift_hours('morning_shift')
        afternoon_hours = self.get_shift_hours('afternoon_shift')
        night_hours = self.get_shift_hours('night_shift')

        # 在日志中记录各班次工作时长
        self.logger.info(f"早班工作时长: {morning_hours} 小时")
        self.logger.info(f"中班工作时长: {afternoon_hours} 小时")
        self.logger.info(f"夜班工作时长: {night_hours} 小时")

        # 计算日班（早班+中班）的总时长
        day_hours = morning_hours + afternoon_hours

        # 基于黄金分割比例分配人力
        day_ratio = self.day_night_ratio / (1 + self.day_night_ratio)
        night_ratio = 1 / (1 + self.day_night_ratio)

        # 计算各班次所需人数（考虑预留资源）
        available_caregivers = max(1, int(total_caregivers * (1 - self.reserve_resource_ratio)))

        self.logger.info(f"总护理员数量: {total_caregivers}, 可用护理员数量: {available_caregivers}")

        # 关键改变：无论护理员数量多少，我们都要确保三个班次都有人
        # 如果可用护理员少于3人，则允许轮班（同一个护理员可以在同一天内担任多个班次）
        if available_caregivers < 3:
            self.logger.info(f"可用护理员少于3人，启用轮班模式")

            # 确保每个班次至少有一个人
            result = {
                'morning_shift': 1,
                'afternoon_shift': 1,
                'night_shift': 1
            }

            self.logger.info(f"轮班模式下的班次分配结果: {result}")
            return result

        # 如果护理员足够，按比例分配
        # 日班人员分配给早班和中班
        day_caregivers = max(1, int(available_caregivers * day_ratio))
        morning_caregivers = max(1, int(day_caregivers * (morning_hours / day_hours)))
        afternoon_caregivers = max(1, day_caregivers - morning_caregivers)

        # 夜班人员
        night_caregivers = max(1, int(available_caregivers * night_ratio))

        # 确保总人数不超过可用护理员数量
        total_assigned = morning_caregivers + afternoon_caregivers + night_caregivers

        if total_assigned > available_caregivers:
            # 如果总人数超过可用人数，按比例减少
            excess = total_assigned - available_caregivers

            # 从人数最多的班次开始减少
            shifts = [('morning_shift', morning_caregivers),
                      ('afternoon_shift', afternoon_caregivers),
                      ('night_shift', night_caregivers)]
            # 先按人数排序，人数相同时按班次顺序排序
            shifts.sort(key=lambda x: (-x[1], self.shift_config[x[0]]['order']))

            for i in range(excess):
                shift_name = shifts[i % len(shifts)][0]
                if shift_name == 'morning_shift' and morning_caregivers > 1:
                    morning_caregivers -= 1
                elif shift_name == 'afternoon_shift' and afternoon_caregivers > 1:
                    afternoon_caregivers -= 1
                elif shift_name == 'night_shift' and night_caregivers > 1:
                    night_caregivers -= 1

        result = {
            'morning_shift': morning_caregivers,
            'afternoon_shift': afternoon_caregivers,
            'night_shift': night_caregivers
        }

        self.logger.info(f"班次分配结果: {result}, 总分配人数: {sum(result.values())}")
        return result

    def adjust_shift_times(self, shift_start_time=None):
        """调整班次时间配置，确保班次时间完全连续无空档

        改进的算法，确保：
        1. 班次时间完全连续，无空档
        2. 每个班次持续8小时，总共覆盖24小时
        3. 正确处理夜班跨天情况
        """
        # 设置每个班次的标准时长（每班8小时）
        morning_hours = 8
        afternoon_hours = 8
        night_hours = 8

        # 如果没有指定起始时间，使用默认的早班开始时间
        if not shift_start_time:
            shift_start_time = self.shift_config['morning_shift']['start_time']
            self.logger.info(f"使用默认早班开始时间: {shift_start_time}")

        # 解析起始时间
        morning_start = self.parse_time(shift_start_time)
        self.logger.info(f"早班开始时间: {morning_start}")

        # 计算早班结束时间（开始时间 + 8小时）
        morning_start_dt = datetime.combine(date.today(), morning_start)
        morning_end_dt = morning_start_dt + timedelta(hours=morning_hours)
        morning_end = morning_end_dt.time()
        self.logger.info(f"早班结束时间: {morning_end}")

        # 计算中班时间（开始时间 = 早班结束时间）
        afternoon_start = morning_end
        afternoon_start_dt = morning_end_dt
        afternoon_end_dt = afternoon_start_dt + timedelta(hours=afternoon_hours)
        afternoon_end = afternoon_end_dt.time()
        self.logger.info(f"中班时间: {afternoon_start} - {afternoon_end}")

        # 计算夜班时间（开始时间 = 中班结束时间）
        night_start = afternoon_end
        night_start_dt = afternoon_end_dt
        night_end_dt = night_start_dt + timedelta(hours=night_hours)

        # 处理夜班跨天情况
        night_end = night_end_dt.time()
        is_overnight = night_end_dt.date() > date.today()
        self.logger.info(f"夜班时间: {night_start} - {night_end} (跨天: {is_overnight})")

        # 创建新的班次配置
        adjusted_config = {
            'morning_shift': {
                'start_time': morning_start.strftime("%H:%M"),
                'end_time': morning_end.strftime("%H:%M"),
                'name': self.shift_config['morning_shift']['name']
            },
            'afternoon_shift': {
                'start_time': afternoon_start.strftime("%H:%M"),
                'end_time': afternoon_end.strftime("%H:%M"),
                'name': self.shift_config['afternoon_shift']['name']
            },
            'night_shift': {
                'start_time': night_start.strftime("%H:%M"),
                'end_time': night_end.strftime("%H:%M"),
                'name': self.shift_config['night_shift']['name']
            }
        }

        # 检查班次时间是否连续
        self.logger.info(f"班次时间配置: {adjusted_config}")
        return adjusted_config

    def match_caregiver_to_elder_needs(self, caregivers, elder_actions):
        """根据老人需求匹配最合适的护理员

        匹配策略：
        1. 首先考虑技能匹配度（能够满足老人需求的技能数量）
        2. 其次考虑护理员的工作负荷（已分配的时间段数量）
        3. 最后考虑护理员的技能多样性（避免浪费高技能护理员）
        """
        # 将老人需要的护理动作转换为整数列表
        elder_action_ids = self.parse_skills(elder_actions)
        if not elder_action_ids:
            self.logger.warning(f"老人需求为空：{elder_actions}")
            return None

        # 记录所有匹配的护理员及其分数
        matched_caregivers = []

        for caregiver in caregivers:
            # 解析护理员技能
            caregiver_skills = self.parse_skills(caregiver.skills)
            if not caregiver_skills:
                self.logger.warning(f"护理员 {caregiver.attendant_name} 没有技能记录")
                continue

            # 计算匹配分数（匹配的技能数量）
            match_score = sum(1 for action in elder_action_ids if action in caregiver_skills)

            # 如果有匹配的技能，则添加到候选列表
            if match_score > 0:
                # 计算护理员的工作负荷（已分配的时间段数量）
                workload = TimeSlot.query.filter_by(
                    caregiver_id=caregiver.id,
                    schedule_date=date.today()
                ).count()

                # 计算技能多样性分数（技能数量与匹配分数的比值）
                # 这个分数越高，表示护理员有更多不需要的技能，应该优先保留给更需要的老人
                skill_diversity = len(caregiver_skills) / max(match_score, 1)

                matched_caregivers.append({
                    'caregiver': caregiver,
                    'match_score': match_score,
                    'workload': workload,
                    'skill_diversity': skill_diversity
                })

        if not matched_caregivers:
            self.logger.warning(f"没有找到匹配的护理员来满足老人需求: {elder_actions}")
            return None

        # 按匹配分数降序排序
        matched_caregivers.sort(key=lambda x: (-x['match_score'], x['workload'], x['skill_diversity']))

        # 返回最佳匹配
        best_match = matched_caregivers[0]['caregiver']
        self.logger.info(f"为老人需求 {elder_actions} 匹配到护理员 {best_match.attendant_name}")
        return best_match

    def check_caregiver_availability(self, caregiver_id, target_date, time_slot):
        """检查护理员在指定时间段是否可用

        注意：此方法已修改，不再从数据库查询，而是使用内存中的可用性数据
        """
        # 使用内存中的字典检查护理员可用性
        if not hasattr(self, '_caregiver_availability'):
            self._caregiver_availability = {}

        # 使用元组作为字典键
        key = (caregiver_id, target_date.isoformat(), time_slot)

        # 如果键不存在或者值为True，则该护理员在该时间段可用
        return self._caregiver_availability.get(key, True)

    def update_caregiver_availability(self, caregiver_id, target_date, time_slot, is_available=False):
        """更新护理员在指定时间段的可用性

        注意：此方法已修改，不再向caregiver_schedules表写入数据
        仅在内存中跟踪护理员可用性，用于排班算法内部使用
        """
        # 使用内存中的字典跟踪护理员可用性，而不是写入数据库
        if not hasattr(self, '_caregiver_availability'):
            self._caregiver_availability = {}

        # 使用元组作为字典键
        key = (caregiver_id, target_date.isoformat(), time_slot)
        self._caregiver_availability[key] = is_available

        self.logger.info(f"更新护理员 {caregiver_id} 在 {target_date} 时间段 {time_slot} 的可用性为 {is_available}（仅内存中）")

    def optimize_schedule(self, initial_schedule, caregivers):
        """使用算法优化排班方案

        复用原接口的遗传算法和模拟退火算法，优化排班方案

        Args:
            initial_schedule: 初始排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        self.logger.info("开始使用算法优化排班方案...")

        # 初始化种群
        population = [initial_schedule]
        for _ in range(self.population_size - 1):
            # 生成变异的排班方案
            variant = self.create_schedule_variant(initial_schedule, caregivers)
            population.append(variant)

        best_schedule = initial_schedule
        best_fitness = self.evaluate_schedule(best_schedule, caregivers)

        # 遗传算法迭代
        for generation in range(self.phenotype_limit):
            # 计算适应度
            fitness_values = [self.evaluate_schedule(schedule, caregivers) for schedule in population]

            # 找到最佳解
            max_index = fitness_values.index(max(fitness_values))
            if fitness_values[max_index] > best_fitness:
                best_schedule = population[max_index]
                best_fitness = fitness_values[max_index]

            # 选择父代
            parents = self.select_parents(population, fitness_values)

            # 交叉和变异
            new_population = []
            for i in range(0, len(parents), 2):
                if i + 1 < len(parents):
                    # 交叉
                    if random.random() < self.crossover_rate:
                        child1, child2 = self.crossover(parents[i], parents[i+1])
                        new_population.extend([child1, child2])
                    else:
                        new_population.extend([parents[i], parents[i+1]])

            # 变异
            for i in range(len(new_population)):
                if random.random() < self.mutation_rate:
                    new_population[i] = self.mutate(new_population[i], caregivers)

            # 模拟退火优化
            for i in range(len(new_population)):
                new_population[i] = self.simulated_annealing(new_population[i], caregivers)

            # 保留精英
            elite_count = self.population_size - self.elimination_size
            sorted_indices = sorted(range(len(fitness_values)), key=lambda i: fitness_values[i], reverse=True)
            elite = [population[i] for i in sorted_indices[:elite_count]]

            # 更新种群
            population = elite + new_population[:self.population_size - len(elite)]

            # 更新温度
            self.temperature *= self.cooling_rate

            if generation % 10 == 0:
                self.logger.info(f"第 {generation} 代，最佳适应度: {best_fitness}, 当前温度: {self.temperature:.2f}")

        self.logger.info(f"算法优化完成，最终适应度: {best_fitness}")
        return best_schedule

    def create_schedule_variant(self, schedule, caregivers):
        """创建排班方案的变异

        Args:
            schedule: 原始排班方案
            caregivers: 护理员列表

        Returns:
            变异的排班方案
        """
        # 深拷贝原始排班方案
        variant = {
            'shift_assignments': schedule['shift_assignments'].copy(),
            'elder_schedules': {k: v.copy() for k, v in schedule['elder_schedules'].items()},
            'time_slot_assignments': schedule['time_slot_assignments'].copy() if 'time_slot_assignments' in schedule else []
        }

        # 随机修改班次分配
        if variant['shift_assignments']:
            # 随机选择一些班次分配进行修改
            num_to_modify = max(1, int(len(variant['shift_assignments']) * 0.2))  # 修改约20%的班次分配
            indices_to_modify = random.sample(range(len(variant['shift_assignments'])), num_to_modify)

            for idx in indices_to_modify:
                # 随机选择一个新的护理员
                new_caregiver = random.choice(caregivers)
                variant['shift_assignments'][idx].caregiver_id = new_caregiver.id
                variant['shift_assignments'][idx].caregiver_name = new_caregiver.attendant_name

        return variant

    def evaluate_schedule(self, schedule, caregivers):
        """评估排班方案的质量

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            适应度分数
        """
        score = 0

        # 1. 评估班次覆盖率
        if 'shift_assignments' in schedule and schedule['shift_assignments']:
            # 按日期和班次类型分组
            shifts_by_date = {}
            for assignment in schedule['shift_assignments']:
                date_key = assignment.shift_date.isoformat() if hasattr(assignment.shift_date, 'isoformat') else str(assignment.shift_date)
                if date_key not in shifts_by_date:
                    shifts_by_date[date_key] = set()
                shifts_by_date[date_key].add(assignment.shift_type)

            # 计算每天的覆盖率
            for date_key, shift_types in shifts_by_date.items():
                coverage = len(shift_types) / 3  # 假设每天有三个班次
                score += coverage * 100  # 满分100

        # 2. 评估护理员工作负载平衡度
        if 'shift_assignments' in schedule and schedule['shift_assignments']:
            # 统计每个护理员的班次数
            caregiver_shifts = {}
            for assignment in schedule['shift_assignments']:
                caregiver_id = assignment.caregiver_id
                caregiver_shifts[caregiver_id] = caregiver_shifts.get(caregiver_id, 0) + 1

            # 计算标准差
            if caregiver_shifts:
                shift_counts = list(caregiver_shifts.values())
                mean_shifts = sum(shift_counts) / len(shift_counts)
                std_dev = (sum((count - mean_shifts) ** 2 for count in shift_counts) / len(shift_counts)) ** 0.5

                # 标准差越小，平衡度越高
                balance_score = 50 / (1 + std_dev)  # 最高分值50
                score += balance_score

        # 3. 评估技能匹配度
        if 'time_slot_assignments' in schedule and schedule['time_slot_assignments']:
            total_matches = 0
            total_slots = len(schedule['time_slot_assignments'])

            if total_slots > 0:
                for slot in schedule['time_slot_assignments']:
                    # 找到该时间段的护理员
                    caregiver = next((c for c in caregivers if c.id == slot.caregiver_id), None)
                    if caregiver:
                        # 检查护理员技能是否匹配活动需求
                        caregiver_skills = self.parse_skills(caregiver.skills)
                        activity_id = int(slot.activity_id) if hasattr(slot, 'activity_id') and slot.activity_id else 0

                        if activity_id in caregiver_skills:
                            total_matches += 1

                skill_match_score = (total_matches / total_slots) * 50  # 最高分值50
                score += skill_match_score

        return score

    def select_parents(self, population, fitness_values):
        """选择父代

        Args:
            population: 种群
            fitness_values: 适应度分数

        Returns:
            选中的父代
        """
        # 使用轮盘赋值选择
        total_fitness = sum(fitness_values)
        if total_fitness == 0:
            # 如果所有适应度都为0，则随机选择
            return random.sample(population, len(population) // 2)

        selection_probs = [f / total_fitness for f in fitness_values]
        selected_indices = random.choices(range(len(population)), weights=selection_probs, k=len(population) // 2)
        return [population[i] for i in selected_indices]

    def crossover(self, parent1, parent2):
        """交叉操作

        Args:
            parent1: 父代一
            parent2: 父代二

        Returns:
            两个子代
        """
        # 对班次分配进行交叉
        if 'shift_assignments' in parent1 and 'shift_assignments' in parent2:
            # 随机选择交叉点
            crossover_point = random.randint(1, min(len(parent1['shift_assignments']), len(parent2['shift_assignments'])) - 1)

            # 创建子代
            child1 = {
                'shift_assignments': parent1['shift_assignments'][:crossover_point] + parent2['shift_assignments'][crossover_point:],
                'elder_schedules': parent1['elder_schedules'].copy(),
                'time_slot_assignments': parent1['time_slot_assignments'].copy() if 'time_slot_assignments' in parent1 else []
            }

            child2 = {
                'shift_assignments': parent2['shift_assignments'][:crossover_point] + parent1['shift_assignments'][crossover_point:],
                'elder_schedules': parent2['elder_schedules'].copy(),
                'time_slot_assignments': parent2['time_slot_assignments'].copy() if 'time_slot_assignments' in parent2 else []
            }

            return child1, child2

        # 如果没有班次分配，直接返回父代
        return parent1, parent2

    def mutate(self, schedule, caregivers):
        """变异操作

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            变异后的排班方案
        """
        # 深拷贝排班方案
        mutated = {
            'shift_assignments': schedule['shift_assignments'].copy(),
            'elder_schedules': {k: v.copy() for k, v in schedule['elder_schedules'].items()},
            'time_slot_assignments': schedule['time_slot_assignments'].copy() if 'time_slot_assignments' in schedule else []
        }

        # 变异班次分配
        if mutated['shift_assignments']:
            # 随机选择一个班次分配进行变异
            idx = random.randint(0, len(mutated['shift_assignments']) - 1)

            # 随机选择一个新的护理员
            new_caregiver = random.choice(caregivers)
            mutated['shift_assignments'][idx].caregiver_id = new_caregiver.id
            mutated['shift_assignments'][idx].caregiver_name = new_caregiver.attendant_name

        return mutated

    def simulated_annealing(self, schedule, caregivers):
        """模拟退火优化

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        current = schedule
        current_fitness = self.evaluate_schedule(current, caregivers)

        # 进行多次尝试
        for _ in range(10):  # 限制尝试次数
            # 生成领域解
            neighbor = self.mutate(current, caregivers)
            neighbor_fitness = self.evaluate_schedule(neighbor, caregivers)

            # 计算适应度差异
            delta = neighbor_fitness - current_fitness

            # 如果领域解更好，或者满足模拟退火接受条件，则接受领域解
            if delta > 0 or random.random() < math.exp(delta / self.temperature):
                current = neighbor
                current_fitness = neighbor_fitness

        return current

    def generate_daily_schedule(self, target_date, caregivers, elders, shift_start_time=None):
        """生成单日排班，同时考虑老人需求和护理员技能

        改进的算法，实现轮班制：
        1. 确保每个班次都至少有一个护理员
        2. 允许同一个护理员在同一天内担任多个班次，但要确保有足够的休息时间
        3. 保留预留资源的概念，但不影响基本的班次覆盖
        """
        # 如果提供了开始时间，调整班次时间
        current_shift_config = self.adjust_shift_times(shift_start_time)
        self.logger.info(f"班次时间配置: {current_shift_config}")

        # 获取当前启用的班次
        active_shifts = [shift for shift in self.shift_control['enabled_shifts']
                        if shift in current_shift_config]

        if not active_shifts:
            self.logger.warning("没有启用的班次，无法生成排班")
            return {'shift_assignments': [], 'schedule_results': [], 'time_slots': []}

        self.logger.info(f"当前启用的班次: {active_shifts}")

        # 计算各班次所需人数
        required_caregivers = self.calculate_required_caregivers(len(caregivers))
        self.logger.info(f"各班次所需护理员数量: {required_caregivers}")

        # 检查是否需要启用轮班模式
        total_required = sum(required_caregivers.values())
        is_rotation_mode = total_required > len(caregivers)

        if is_rotation_mode:
            self.logger.info(f"所需护理员总数 {total_required} 超过可用护理员数量 {len(caregivers)}，启用轮班模式")

        # 根据排班次数排序护理员，优先分配排班次数少的护理员
        # 这样可以确保预留资源不会总是同一个人
        if hasattr(self, '_caregiver_assignment_count'):
            sorted_caregivers = sorted(caregivers, key=lambda c: self._caregiver_assignment_count.get(c.id, 0))
        else:
            # 如果还没有跟踪数据，则随机排序
            sorted_caregivers = list(caregivers)
            random.shuffle(sorted_caregivers)

        # 跟踪已分配班次的护理员，确保同一天不会分配过多班次
        assigned_caregivers = {}

        # 班次分配结果
        shift_assignments = []

        # 为每个班次分配护理员
        # 按照班次顺序进行分配
        shift_types = ['morning_shift', 'afternoon_shift', 'night_shift']
        # 根据 order 字段排序
        shift_types.sort(key=lambda x: self.shift_config[x]['order'])
        for shift_type in shift_types:
            num_required = required_caregivers[shift_type]
            # 获取中文班次名称
            shift_name_cn = self.shift_config[shift_type]['name']
            self.logger.info(f"开始为 {shift_name_cn} 分配 {num_required} 个护理员")

            # 获取班次时间
            shift_start = self.parse_time(current_shift_config[shift_type]['start_time'])
            shift_end = self.parse_time(current_shift_config[shift_type]['end_time'])

            # 处理夜班跨天情况
            is_overnight = False
            if shift_type == 'night_shift' and shift_end < shift_start:
                is_overnight = True
                self.logger.info(f"夜班是跨天班次: {shift_start} - {shift_end}")

            # 分配护理员到这个班次
            assigned_count = 0

            # 如果在轮班模式下，我们需要确保每个班次至少有一个人
            if is_rotation_mode and num_required > 0:
                # 在轮班模式下，我们需要找到最适合这个班次的护理员
                # 优先考虑还没有被分配班次的护理员
                available_caregivers = [c for c in sorted_caregivers if c.id not in assigned_caregivers]

                # 如果没有可用的护理员，则考虑已经被分配了一个班次的护理员
                if not available_caregivers:
                    available_caregivers = [c for c in sorted_caregivers if assigned_caregivers.get(c.id, 0) < 2]

                # 如果还是没有可用的护理员，则使用所有护理员
                if not available_caregivers:
                    available_caregivers = sorted_caregivers

                # 选择第一个可用的护理员
                if available_caregivers:
                    caregiver = available_caregivers[0]

                    # 更新已分配班次计数
                    assigned_caregivers[caregiver.id] = assigned_caregivers.get(caregiver.id, 0) + 1

                    # 获取中文班次名称
                    shift_name_cn = self.shift_config[shift_type]['name']

                    # 创建班次分配记录
                    assignment = ShiftAssignment(
                        id=str(uuid.uuid4()),
                        caregiver_id=caregiver.id,
                        caregiver_name=caregiver.attendant_name,
                        shift_date=target_date,
                        shift_type=shift_name_cn,  # 使用中文班次名称
                        shift_start_time=shift_start,
                        shift_end_time=shift_end,
                        schedule_type='daily'
                    )

                    shift_assignments.append(assignment)
                    assigned_count += 1
                    self.logger.info(f"轮班模式: 分配护理员 {caregiver.attendant_name} 到 {shift_name_cn}，时间 {shift_start}-{shift_end}{' (跨天)' if is_overnight else ''}")
            else:
                # 正常模式，为这个班次分配指定数量的护理员
                for _ in range(num_required):
                    # 找到还没有被分配班次的护理员
                    available_caregivers = [c for c in sorted_caregivers if c.id not in assigned_caregivers]

                    if available_caregivers:
                        caregiver = available_caregivers[0]

                        # 标记该护理员已分配班次
                        assigned_caregivers[caregiver.id] = 1

                        # 获取中文班次名称
                        shift_name_cn = self.shift_config[shift_type]['name']

                        # 创建班次分配记录
                        assignment = ShiftAssignment(
                            id=str(uuid.uuid4()),
                            caregiver_id=caregiver.id,
                            caregiver_name=caregiver.attendant_name,
                            shift_date=target_date,
                            shift_type=shift_name_cn,  # 使用中文班次名称
                            shift_start_time=shift_start,
                            shift_end_time=shift_end,
                            schedule_type='daily'
                        )

                        shift_assignments.append(assignment)
                        assigned_count += 1
                        self.logger.info(f"分配护理员 {caregiver.attendant_name} 到 {shift_name_cn}，时间 {shift_start}-{shift_end}{' (跨天)' if is_overnight else ''}")

            if assigned_count < num_required:
                self.logger.warning(f"{shift_name_cn} 实际分配了 {assigned_count} 个护理员，少于需求的 {num_required} 个")

        # 根据老人需求和护理员技能生成详细的时间段安排
        elder_schedules = {}
        time_slot_assignments = []

        # 为每个老人生成排班
        for elder in elders:
            # 创建排班结果记录
            schedule_result = ScheduleResult(
                id=str(uuid.uuid4()),
                elder=elder.elder,
                schedule_date=target_date,
                schedule_type='three_shift',  # 指定为三班制排班
                schedule_data={}  # 初始化空数据，后面会更新
            )

            # 解析老人需要的护理动作
            elder_actions = elder.actions

            # 为每个时间段分配护理员和活动
            daily_slots = []
            for time_slot in range(self.time_slots_per_day):
                # 计算实际时间
                hours = (time_slot * self.time_interval_minutes) // 60
                minutes = (time_slot * self.time_interval_minutes) % 60
                time_str = f"{hours:02d}:{minutes:02d}"

                # 找到当前时间段对应的班次和可用护理员
                current_shift = None
                current_time = self.parse_time(time_str)
                self.logger.info(f"检查时间段 {time_str} 对应的班次")

                for shift_type in ['morning_shift', 'afternoon_shift', 'night_shift']:
                    shift_start = self.parse_time(current_shift_config[shift_type]['start_time'])
                    shift_end = self.parse_time(current_shift_config[shift_type]['end_time'])

                    # 处理跨天情况
                    if shift_type == 'night_shift' and shift_end < shift_start:
                        # 夜班跨天：如果当前时间小于结束时间或大于等于开始时间，则在夜班内
                        if current_time < shift_end or current_time >= shift_start:
                            current_shift = shift_type
                            self.logger.info(f"时间 {time_str} 匹配夜班(跨天): {shift_start}-{shift_end}")
                            break
                    else:
                        # 普通情况：如果当前时间在班次时间范围内，则匹配
                        if shift_start <= current_time < shift_end:
                            current_shift = shift_type
                            self.logger.info(f"时间 {time_str} 匹配{shift_type}: {shift_start}-{shift_end}")
                            break

                if not current_shift:
                    self.logger.warning(f"时间 {time_str} 不在任何班次内，跳过")
                    continue  # 跳过不在任何班次内的时间段

                # 找到当前班次的护理员
                shift_caregivers = [a for a in shift_assignments if a.shift_type == current_shift.split('_')[0]]
                available_caregivers = []

                for assignment in shift_caregivers:
                    caregiver = next((c for c in caregivers if c.id == assignment.caregiver_id), None)
                    if caregiver and self.check_caregiver_availability(caregiver.id, target_date, time_slot):
                        available_caregivers.append(caregiver)

                # 根据老人需求匹配最合适的护理员
                matched_caregiver = self.match_caregiver_to_elder_needs(available_caregivers, elder_actions)

                if matched_caregiver:
                    # 从老人需求中选择一个护理动作
                    action_ids = self.parse_skills(elder_actions)
                    caregiver_skills = self.parse_skills(matched_caregiver.skills)

                    # 找到护理员能够执行的第一个护理动作
                    selected_action_id = next((action for action in action_ids if action in caregiver_skills), None)

                    if selected_action_id:
                        # 获取护理动作详情
                        nursing_actions = self.get_nursing_actions()
                        action_name = nursing_actions.get(selected_action_id, DicNursingAction(name="未知活动")).name

                        # 创建时间段记录
                        time_slot_record = TimeSlot(
                            id=str(uuid.uuid4()),
                            schedule_result_id=schedule_result.id,
                            elder=elder.elder,
                            time_slot=time_str,
                            activity=action_name,
                            activity_id=str(selected_action_id),
                            caregiver=matched_caregiver.attendant_name,
                            caregiver_id=matched_caregiver.id,
                            time_order=time_slot,
                            schedule_date=target_date
                        )

                        daily_slots.append(time_slot_record)
                        time_slot_assignments.append(time_slot_record)

                        # 更新护理员可用性
                        self.update_caregiver_availability(matched_caregiver.id, target_date, time_slot, False)

            # 更新排班结果的JSON数据
            schedule_result.schedule_data = {
                'elder': elder.elder,
                'date': target_date.isoformat(),
                'slots_count': len(daily_slots)
            }

            elder_schedules[elder.elder] = {
                'schedule_result': schedule_result,
                'time_slots': daily_slots
            }

        return {
            'shift_assignments': shift_assignments,
            'elder_schedules': elder_schedules,
            'time_slot_assignments': time_slot_assignments
        }

    def generate_schedule(self, schedule_type='daily', num_days=1, start_date=None, shift_start_time=None):
        """生成排班计划

        参数:
            schedule_type: 排班类型（'daily', 'weekly', 'monthly', 'quarterly', 'yearly'）
            num_days: 当选择daily时，指定天数
            start_date: 开始日期，默认为今天
            shift_start_time: 班次开始时间，格式为'HH:MM'
        """
        if not start_date:
            start_date = date.today()
        elif isinstance(start_date, str):
            start_date = datetime.strptime(start_date, "%Y-%m-%d").date()

        # 根据排班类型确定结束日期
        if schedule_type == 'daily':
            end_date = start_date + timedelta(days=num_days - 1)
        elif schedule_type == 'weekly':
            end_date = start_date + timedelta(days=6)  # 一周7天
        elif schedule_type == 'monthly':
            # 计算下个月的同一天
            if start_date.month == 12:
                end_date = date(start_date.year + 1, 1, start_date.day)
            else:
                # 处理月末日期问题
                next_month = start_date.month + 1
                last_day = 31  # 默认最大天数
                while True:
                    try:
                        end_date = date(start_date.year, next_month, last_day)
                        break
                    except ValueError:
                        last_day -= 1
        elif schedule_type == 'quarterly':
            # 计算三个月后的同一天
            month = start_date.month + 3
            year = start_date.year
            if month > 12:
                month -= 12
                year += 1
            # 处理月末日期问题
            last_day = 31  # 默认最大天数
            while True:
                try:
                    end_date = date(year, month, last_day)
                    break
                except ValueError:
                    last_day -= 1
        elif schedule_type == 'yearly':
            end_date = date(start_date.year + 1, start_date.month, start_date.day) - timedelta(days=1)
        else:
            raise ValueError(f"不支持的排班类型: {schedule_type}")

        # 获取所有护理员和需要安排的老人
        all_caregivers = self.get_caregivers()
        all_elders = self.get_elders()

        if not all_caregivers:
            raise ValueError("没有可用的护理员")

        if not all_elders:
            raise ValueError("没有需要安排的老人")

        # 生成每一天的排班
        all_results = {
            'shift_assignments': [],  # 班次分配
            'schedule_results': [],   # 排班结果
            'time_slots': [],         # 时间段分配
            'elder_schedules': {}     # 老人排班计划，用于中间处理
        }

        current_date = start_date

        # 初始化或获取护理员排班次数跟踪器
        if not hasattr(self, '_caregiver_assignment_count'):
            self._caregiver_assignment_count = {caregiver.id: 0 for caregiver in all_caregivers}
            self.logger.info(f"初始化护理员排班计数: {self._caregiver_assignment_count}")

        # 跟踪每日排班的护理员，实现轮班制
        while current_date <= end_date:
            self.logger.info(f"开始生成 {current_date} 的排班计划")
            self.logger.info(f"当前护理员排班次数: {self._caregiver_assignment_count}")

            # 生成当天的排班
            daily_results = self.generate_daily_schedule(
                current_date,
                all_caregivers,  # 传入所有护理员，在方法内部会根据排班次数排序
                all_elders,
                shift_start_time
            )

            # 更新护理员排班次数
            for assignment in daily_results['shift_assignments']:
                caregiver_id = assignment.caregiver_id
                self._caregiver_assignment_count[caregiver_id] = self._caregiver_assignment_count.get(caregiver_id, 0) + 1
                self.logger.info(f"护理员 {assignment.caregiver_name} 排班次数增加到 {self._caregiver_assignment_count[caregiver_id]}")

            # 合并结果
            all_results['shift_assignments'].extend(daily_results['shift_assignments'])

            # 添加老人排班结果
            for elder_id, elder_schedule in daily_results['elder_schedules'].items():
                # 添加排班结果和时间段
                all_results['schedule_results'].append(elder_schedule['schedule_result'])
                all_results['time_slots'].extend(elder_schedule['time_slots'])

                # 将老人排班计划存入elder_schedules字典，以便后续处理
                if elder_id not in all_results['elder_schedules']:
                    all_results['elder_schedules'][elder_id] = {
                        'schedule_results': [],
                        'time_slots': []
                    }

                all_results['elder_schedules'][elder_id]['schedule_results'].append(elder_schedule['schedule_result'])
                all_results['elder_schedules'][elder_id]['time_slots'].extend(elder_schedule['time_slots'])

            current_date += timedelta(days=1)

        # 输出最终的护理员排班统计
        self.logger.info(f"最终护理员排班统计:")
        for caregiver in all_caregivers:
            self.logger.info(f"护理员 {caregiver.attendant_name} (编号: {caregiver.id}) 排班次数: {self._caregiver_assignment_count[caregiver.id]}")

        # 分析排班结果
        shift_counts = {'班次1': 0, '班次2': 0, '班次3': 0}
        for assignment in all_results['shift_assignments']:
            if assignment.shift_type in shift_counts:
                shift_counts[assignment.shift_type] += 1

        self.logger.info(f"排班统计: 班次1 {shift_counts['班次1']} 人次, 班次2 {shift_counts['班次2']} 人次, 班次3 {shift_counts['班次3']} 人次")

        return all_results

    def save_shift_assignments(self, shift_assignments):
        """仅保存班次分配到数据库

        Args:
            shift_assignments: 班次分配列表

        Returns:
            bool: 是否保存成功
        """
        try:
            # 开始事务
            db.session.begin()

            # 保存班次分配到shift_assignments表
            for assignment in shift_assignments:
                db.session.add(assignment)

            # 提交事务
            db.session.commit()

            # 记录保存的数据
            self.logger.info(f"成功保存排班计划:")
            self.logger.info(f"  - {len(shift_assignments)} 个班次分配到shift_assignments表")
            self.logger.info("重要：护理员可用性数据仅在内存中跟踪，不写入caregiver_schedules表")
            return True
        except Exception as e:
            db.session.rollback()
            self.logger.error(f"保存班次分配失败: {str(e)}")
            return False

    def save_schedule(self, schedule_results):
        """保存排班计划到数据库

        保存数据到以下表：
        - shift_assignments表：班次分配
        - schedule_results表：排班结果
        - time_slots表：时间段分配

        注意：不会向caregiver_schedules表写入数据
        """
        try:
            # 开始事务
            db.session.begin()

            # 1. 保存班次分配到shift_assignments表
            for assignment in schedule_results['shift_assignments']:
                db.session.add(assignment)

            # 2. 保存排班结果到schedule_results表
            schedule_results_count = 0
            for elder_schedule in schedule_results.get('elder_schedules', {}).values():
                schedule_result = elder_schedule.get('schedule_result')
                if schedule_result:
                    db.session.add(schedule_result)
                    schedule_results_count += 1

            # 3. 保存时间段分配到time_slots表
            time_slots_count = 0
            for elder_schedule in schedule_results.get('elder_schedules', {}).values():
                time_slots = elder_schedule.get('time_slots', [])
                for time_slot in time_slots:
                    db.session.add(time_slot)
                    time_slots_count += 1

            # 提交事务
            db.session.commit()

            # 记录保存的数据
            self.logger.info(f"成功保存排班计划:")
            self.logger.info(f"  - {len(schedule_results['shift_assignments'])} 个班次分配到shift_assignments表")
            self.logger.info(f"  - {schedule_results_count} 个排班结果到schedule_results表")
            self.logger.info(f"  - {time_slots_count} 个时间段分配到time_slots表")
            self.logger.info("重要：护理员可用性数据仅在内存中跟踪，不写入caregiver_schedules表")
            return True
        except Exception as e:
            db.session.rollback()
            self.logger.error(f"保存排班计划失败: {str(e)}")
            return False

    def generate_and_save_schedule(self, schedule_type=None, num_days=None, start_date=None, shift_start_time=None, generate_care_timeline=None):
        """生成并保存排班计划

        Args:
            schedule_type: 排班类型，1(按天), 2(按周), 3(按月), 4(按季度), 5(按年)
            num_days: 排班天数，当schedule_type为1(按天)时有效
            start_date: 开始日期，如果为None则使用全局参数或当前日期
            shift_start_time: 班次开始时间
            generate_care_timeline: 是否生成照护时间线，如果为None，则使用全局参数
        """
        from config_params import three_shift_params

        # 使用全局参数作为默认值
        if schedule_type is None:
            schedule_type = three_shift_params.get('schedule_type', 1)

        if num_days is None:
            num_days = three_shift_params.get('num_days', 1)

        if start_date is None:
            start_date = three_shift_params.get('start_date')

        if shift_start_time is None:
            shift_start_time = three_shift_params.get('shift_start_time', '06:00')

        if generate_care_timeline is None:
            generate_care_timeline = three_shift_params.get('auto_schedule_generate_care_timeline', True)

        # 将数字排班类型转换为字符串类型（与原有代码兼容）
        schedule_type_map = {
            1: 'daily',
            2: 'weekly',
            3: 'monthly',
            4: 'quarterly',
            5: 'yearly'
        }

        # 如果是数字类型，转换为字符串
        if isinstance(schedule_type, int):
            schedule_type_str = schedule_type_map.get(schedule_type)
            if schedule_type_str is None:
                self.logger.warning(f"不支持的排班类型数字: {schedule_type}, 使用默认值'daily'")
                schedule_type_str = 'daily'
        else:
            # 如果已经是字符串，直接使用
            schedule_type_str = schedule_type

        self.logger.info(f"开始生成排班计划: 类型={schedule_type}({schedule_type_str}), 天数={num_days}, 开始日期={start_date}, 生成照护时间线={generate_care_timeline}")

        # 确保参数类型正确
        try:
            if isinstance(num_days, str):
                num_days = int(num_days)
                self.logger.info(f"将天数从字符串转换为整数: {num_days}")
        except ValueError as e:
            self.logger.warning(f"天数转换失败, 使用默认值1: {str(e)}")
            num_days = 1

        # 生成排班计划
        start_time = datetime.now()
        self.logger.info(f"开始生成排班计划...")
        schedule_results = self.generate_schedule(schedule_type_str, num_days, start_date, shift_start_time)
        end_time = datetime.now()
        generation_time = (end_time - start_time).total_seconds()
        self.logger.info(f"排班计划生成完成, 耗时 {generation_time:.2f} 秒")

        # 如果启用了算法优化，使用算法优化排班方案
        if params.get('use_algorithm_optimization', False):
            self.logger.info("开始使用算法优化排班方案...")
            start_time = datetime.now()
            all_caregivers = self.get_caregivers()
            schedule_results = self.optimize_schedule(schedule_results, all_caregivers)
            end_time = datetime.now()
            optimization_time = (end_time - start_time).total_seconds()
            self.logger.info(f"算法优化完成, 耗时 {optimization_time:.2f} 秒")

        # 保存到数据库
        self.logger.info(f"开始保存排班计划到数据库...")
        start_time = datetime.now()

        # 根据是否生成照护时间线决定保存的数据
        if not generate_care_timeline:
            # 如果不生成照护时间线，只保存班次分配
            success = self.save_shift_assignments(schedule_results['shift_assignments'])
            self.logger.info(f"仅保存班次分配，不生成照护时间线")
        else:
            # 如果生成照护时间线，保存所有数据
            success = self.save_schedule(schedule_results)
            self.logger.info(f"保存所有数据，包括照护时间线")

        end_time = datetime.now()
        save_time = (end_time - start_time).total_seconds()

        if success:
            self.logger.info(f"排班计划保存成功, 耗时 {save_time:.2f} 秒")
        else:
            self.logger.error(f"排班计划保存失败, 耗时 {save_time:.2f} 秒")

        # 返回结果摘要
        result = {
            'success': success,
            'schedule_type': schedule_type,
            'start_date': start_date.strftime("%Y-%m-%d") if isinstance(start_date, date) else start_date,
            'shift_assignments_count': len(schedule_results['shift_assignments']),
            'generate_care_timeline': generate_care_timeline
        }

        # 如果生成了照护时间线，添加相关统计信息
        if generate_care_timeline:
            result.update({
                'schedule_results_count': len(schedule_results['schedule_results']),
                'time_slots_count': len(schedule_results['time_slots'])
            })

        self.logger.info(f"排班结果摘要: {result}")
        return result
